﻿using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AutoOm.Manage.Om.OmTask;

namespace AutoOm.Manage.Om.OmInvoke
{
    public class OmCmd : IDisposable
    {
        private readonly string _fileName;
        private readonly string _workingDirectory;
        private Process _process;
        private readonly object _objInvoke = new object();
        private readonly StringBuilder _result = new StringBuilder();

        public event TaskEventHandler Process;
        public OmCmd(string fileName = "cmd", string workingDirectory = "")
        {
            _fileName = fileName;
            _workingDirectory = workingDirectory;
            _process = CreateProcess();
            Monitor.Enter(_objInvoke);
            Monitor.Wait(_objInvoke);
        }
        public string Invoke(string cmd)
        {
            Debug.WriteLine(cmd);
            _result.Clear();
            Monitor.Enter(_objInvoke);
            var process = CreateProcess();
            process.StandardInput.WriteLine(cmd);
            Monitor.Wait(_objInvoke);
            return _result.ToString();
        }

        private Process CreateProcess()
        {
            if (_process != null) return _process;
            _process = new Process
            {
                StartInfo =
                {
                    FileName = _fileName,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WorkingDirectory = _workingDirectory
                },
                EnableRaisingEvents = true,
            };
            _process.OutputDataReceived += (sender, args) =>
            {
                _result.AppendLine(args.Data);
                OnProcess(args.Data);
                if (string.IsNullOrEmpty(args.Data))
                {
                    Monitor.Enter(_objInvoke);
                    Monitor.PulseAll(_objInvoke);
                    Monitor.Exit(_objInvoke);
                }
            };
            _process.ErrorDataReceived += (sender, args) =>
            {
                _result.AppendLine(args.Data);
                OnProcess(args.Data);
                if (string.IsNullOrEmpty(args.Data))
                {
                    Monitor.Enter(_objInvoke);
                    Monitor.PulseAll(_objInvoke);
                    Monitor.Exit(_objInvoke);
                }
            };
            _process.Start();
            _process.StandardInput.AutoFlush = true;
            _process.BeginOutputReadLine();
            _process.BeginErrorReadLine();
            Task.Run(() =>
            {
                _process.WaitForExit();
                _process.Close();
            });
            return _process;
        }

        public void Dispose()
        {
            if (_process != null)
            {
                Invoke("exit");
            }
        }

        protected virtual void OnProcess(string log)
        {
            Process?.Invoke(log);
        }
    }
}